#ifdef ENABLE_DB_JUDGE
#include "database_judge.h"
#include "baseJudge.hpp"

#include <mysql/mysql.h>

class DatabaseConnectComponent {
public:
    mutable MYSQL conn;
    bool connected = false;
    ProcessStatus init(std::string host, int port,
                        std::string user, std::string passwd);
    ~DatabaseConnectComponent();
};

template <class T>
class DatabaseJudge : public CorrectOutComponent, public DatabaseConnectComponent, public JudgeType<T> {
public:
    std::string createTableFile;
    std::string database_name;
    DatabaseJudge();
    template <class U>
    ProcessStatus init(run_code_context_t<U>& context);
    template <class Event>
    run_code_event_t next_event(Event e);
    using JudgeType<T>::custom_params;
    decltype(auto) custom_params(run_code_event_space::database_compare_event) const {
        return make_tuple(this->get_compare_file());
    }
};
class DatabaseSelectJudge : public DatabaseJudge<DatabaseSelectJudge> {};
class DatabaseModifyJudge: public DatabaseJudge<DatabaseModifyJudge> {};

template <class T>
DatabaseJudge<T>::DatabaseJudge() {
    this->filter_lang = DATABASE_LANGUAGE;
}

template <class T>
template <class U>
ProcessStatus DatabaseJudge<T>::init(run_code_context_t<U>& context) {
    if (auto res = JudgeType<T>::init(context); res.result != RUN_OK) {
        return res;
    }
    if (auto res = CorrectOutComponent::init(context.params.dataPath); res.result != RUN_OK) {
        return res;
    }
    if (auto res = DatabaseConnectComponent::init(databaseParams.host, databaseParams.port, databaseParams.root, databaseParams.passwd); res.result != RUN_OK) {
        return res;
    }

    auto createTableFiles = searchFiles(context.params.initPath, "create", startWith);
    if (createTableFiles.empty()) {
        return ProcessStatus {  .result = PARAMS_ERROR,
                                .error = "less create table file"};
    }
    this->createTableFile = osJoin(context.params.initPath, createTableFiles[0]);
    
    this->database_name = std::filesystem::path(context.params.initPath).stem().string() +  "." + calc_md5(fileToStr(this->createTableFile));

    return ProcessStatus{ .result = RUN_OK };
}

template <class T>
template <class Event>
run_code_event_t DatabaseJudge<T>::next_event(Event e) {
    using std::is_same_v;
    const auto& context = *this->belong_context;
    if constexpr (is_same_v<Event, initial_event>) {
        if (context.get_latest_status().result != RUN_OK) {
            return end_event{};
        }
        return database_update_event{};
    }
    if constexpr (is_same_v<Event, database_update_event>) {
        if (context.get_latest_status().result != RUN_OK) {
            return end_event{};
        }
        return database_std_sql_run_event{};
    }
    if constexpr (is_same_v<Event, database_std_sql_run_event>) {
        return database_user_sql_run_event{};
    }
    if constexpr (is_same_v<Event, database_user_sql_run_event>) {
        if (context.get_latest_status().result != RUN_OK) {
            return calculate_result_event{};
        }
        return database_compare_event{};
    }
    if constexpr (is_same_v<Event, database_compare_event>) {
        return calculate_result_event{};
    }
    if constexpr (is_same_v<Event, calculate_result_event>) {
        // all test example is tested
        if (this->inFiles.size() == this->example_index) {
            return end_event{};
        // latest test is error
        } else if (context.result.passExample != context.result.testExample) {
            int ruler = context.params.ruler;
            // OI CONTENT continue test
            if (ruler == OI_CONTENT) {
            // ACM CONTENT exit
            } else if (ruler == ACM_CONTENT) {
                return end_event{};
            }
        }
        return database_std_sql_run_event{};
    }
    return end_event{};
}

#endif

